Skip to content

fix(passthrough): stop leaking the caller's virtual key on credential-less Vertex passthrough - #38114

Merged
mateo-berri merged 12 commits into
litellm_internal_stagingfrom
litellm_fix_5997_vertex_pt_key_leak
Aug 24, 2026
Merged

mateo-berri merged 12 commits into
litellm_internal_stagingfrom
litellm_fix_5997_vertex_pt_key_leak

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Credential-less Vertex passthrough forwarded the caller's proxy auth headers to Google
  • Every header the proxy accepts for caller auth leaked upstream: Authorization, x-litellm-api-key, x-goog-api-key, api-key, x-api-key, Ocp-Apim-Subscription-Key, the mapped-route litellm_user_api_key header, and the operator-configured litellm_key_header_name
  • The LiteLLM virtual key, and any other caller secret in those headers, reached a third-party provider

How it solves it:

  • Derive the name-drop set from the canonical SpecialHeaders.litellm_credential_header_names(), so every proxy-only auth header Google never consumes (x-litellm-api-key, api-key, x-api-key, Ocp-Apim-Subscription-Key) is dropped, plus any operator-configured caller-key header (litellm_key_header_name and each pass_through_endpoints entry's litellm_user_api_key), and future additions are covered automatically
  • Resolve the caller's key by the same precedence user_api_key_auth uses and value-strip exactly that value from Authorization / x-goog-api-key (which can instead hold a real Google credential) and from the operator-configured custom key header, normalizing the value with the auth module's own _get_bearer_token so every scheme it accepts (Bearer / bearer / Basic / AWS4-HMAC-SHA256) is matched
  • Fail with a clean 401 when no real Google credential is present, so nothing is forwarded

User Flow

Before: a developer calls Vertex passthrough on a proxy with no Vertex credential configured, and their LiteLLM key is handed to Google

  1. They POST https://litellm-domain/vertex_ai/v1/projects/my-proj/locations/global/publishers/google/models/gemini-2.5-pro:generateContent with Authorization: Bearer sk-... (their LiteLLM virtual key) and a JSON body
  2. The proxy authenticates them, finds no Vertex credential, and forwards the request to https://aiplatform.googleapis.com/v1/projects/my-proj/...:generateContent with that same Authorization: Bearer sk-... still on it
  3. Google receives the developer's LiteLLM virtual key; sending the key in x-litellm-api-key, x-goog-api-key, api-key, x-api-key, or the operator's configured key header instead leaks it the same way
  4. Another party who can read that upstream request now holds a working LiteLLM key and can call the proxy as that developer

After: the same call fails fast with a clean 401, and the key is never forwarded

  1. They POST the same URL with Authorization: Bearer sk-... and the same body
  2. The proxy authenticates them, finds no Vertex credential and no bring-your-own Google credential, and returns 401 saying no Vertex credential is configured and the virtual key is not forwarded
  3. Nothing is sent to https://aiplatform.googleapis.com; sending the key in x-litellm-api-key or x-goog-api-key gives the same 401, and any api-key / x-api-key / configured custom key header is stripped before forwarding
  4. A developer who brings their own Google credential (an OAuth token in Authorization, or a real Google API key in x-goog-api-key) still has it forwarded, now with the LiteLLM key and the other proxy auth headers stripped out
  5. No other party can obtain a LiteLLM virtual key from this branch anymore

Relevant issues

Linear ticket

Resolves LIT-5997

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

Shared setup (no real virtual key is ever sent to real Google: a local sink intercepts every egress and the capture is the proof)

  • Proxy config has no Vertex credential: no DEFAULT_VERTEXAI_* env vars, and no use_in_pass_through model, so the passthrough takes the credential-less branch
  • A local mitmproxy sink sits on the proxy's egress. It intercepts every request whose host ends in googleapis.com, records the exact headers that would have gone to Google, and returns a synthetic 599 so nothing reaches Google. A leak shows up as the virtual key (or any caller secret) appearing in that capture
  • Generate a virtual key against the proxy: curl -sX POST http://127.0.0.1:PORT/key/generate -H "<auth header>: Bearer $MASTER_KEY" -d '{"duration":"2h"}' returns sk-…
  • Payload for every call: {"contents":[{"role":"user","parts":[{"text":"hi"}]}]}
  • The custom-key-header cases (Case G) run against a second proxy configured with general_settings.litellm_key_header_name: x-company-key; every other case runs against a default-config proxy

Before (28b433a)

Case A: virtual key in Authorization

  1. curl -sX POST http://127.0.0.1:49346/vertex_ai/v1/projects/my-proj/locations/global/publishers/google/models/gemini-2.5-pro:generateContent -H "Authorization: Bearer sk-…" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: authorization: Bearer sk-…, the virtual key was on its way to Google

Case B: virtual key in x-litellm-api-key

  1. curl -sX POST http://127.0.0.1:49346/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: x-litellm-api-key: sk-…, the virtual key was on its way to Google

Case D: virtual key in x-goog-api-key

  1. curl -sX POST http://127.0.0.1:49346/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "x-goog-api-key: sk-…" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: x-goog-api-key: sk-… and x-litellm-api-key: sk-…. The virtual key was on its way to Google, this time posing as a Google API key

Case F: distinct caller secrets in api-key and x-api-key

  1. curl -sX POST http://127.0.0.1:49346/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Bearer ya29.fake-google-oauth" -H "api-key: azure-secret-abc123" -H "x-api-key: anthropic-secret-def456" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: authorization: Bearer ya29.fake-google-oauth, api-key: azure-secret-abc123, x-api-key: anthropic-secret-def456, and x-litellm-api-key: sk-…. Every proxy auth header, including two unrelated caller secrets and the virtual key, went to Google

Case G: virtual key in the operator-configured custom key header

Against a merge-base proxy configured with general_settings.litellm_key_header_name: x-company-key

  1. curl -sX POST http://127.0.0.1:42826/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-company-key: Bearer sk-…" -H "x-goog-api-key: AIzaSyReal-Google-Api-Key-000" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: x-company-key: Bearer sk-… and x-goog-api-key: AIzaSyReal-Google-Api-Key-000. The caller authenticated with the custom header, and that same header carrying the virtual key was forwarded to Google alongside the real Google key

Case H: caller Azure APIM secret in Ocp-Apim-Subscription-Key

  1. curl -sX POST http://127.0.0.1:49346/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Bearer ya29.fake-google-oauth" -H "Ocp-Apim-Subscription-Key: azure-apim-secret-xyz789" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: authorization: Bearer ya29.fake-google-oauth and Ocp-Apim-Subscription-Key: azure-apim-secret-xyz789. The caller's Azure APIM subscription key, an auth header the proxy accepts but Google never consumes, was forwarded to Google

Case J: virtual key echoed into Authorization with a Basic scheme

  1. curl -sX POST http://127.0.0.1:49346/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Basic sk-…" -d '<payload>'
  2. Sink capture of the outbound to aiplatform.googleapis.com: authorization: Basic sk-…. The caller authenticated with x-litellm-api-key and echoed the same virtual key into Authorization under a Basic scheme, and that header carrying the key was forwarded to Google

After (16a81c9)

The header-filtering behavior in Cases A-H is stable across the hardening commits and was captured against the default-config proxy on port 41337 and the custom-key-header proxy on port 41779. Case I was run against a fresh default-config proxy on port 40923, and Case J plus the no-regression re-check of Cases C and E against a fresh default-config proxy on port 40611 at this exact tip.

Case A: virtual key in Authorization

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "Authorization: Bearer sk-…" -d '<payload>'
  2. Response: HTTP 401 with {"detail":"No Vertex AI credential is configured on this proxy and the request carried no upstream Google credential. The LiteLLM virtual key is not forwarded to Google. ..."}. The sink recorded nothing new: no request reached googleapis.com

Case B: virtual key in x-litellm-api-key

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -d '<payload>'
  2. Response: HTTP 401 with the same "no Vertex AI credential is configured" detail. The sink recorded nothing new: no request reached googleapis.com

Case C: bring-your-own Google token, virtual key in x-litellm-api-key for proxy auth

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Bearer ya29.fake-google-oauth" -d '<payload>'
  2. Response: HTTP 599 from the sink (the request was forwarded). Sink capture of the outbound to aiplatform.googleapis.com: authorization: Bearer ya29.fake-google-oauth, x-litellm-api-key: <absent>, and the virtual key is nowhere in the outbound. Real bring-your-own passthrough still works, with the key stripped

Case D: virtual key in x-goog-api-key

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "x-goog-api-key: sk-…" -d '<payload>'
  2. Response: HTTP 401 with the same "no Vertex AI credential is configured" detail. The sink recorded nothing new: no request reached googleapis.com. The key posing as a Google API key no longer satisfies the gate

Case E: bring-your-own real Google API key in x-goog-api-key, virtual key in x-litellm-api-key for proxy auth

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "x-goog-api-key: AIzaSyReal-Google-Api-Key-000" -d '<payload>'
  2. Response: HTTP 599 from the sink (the request was forwarded). Sink capture of the outbound to aiplatform.googleapis.com: x-goog-api-key: AIzaSyReal-Google-Api-Key-000, x-litellm-api-key: <absent>, and the virtual key is nowhere in the outbound. A real Google API key that differs from the virtual key still forwards, with the key stripped

Case F: distinct caller secrets in api-key and x-api-key, bring-your-own Google token for the real credential

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Bearer ya29.fake-google-oauth" -H "api-key: azure-secret-abc123" -H "x-api-key: anthropic-secret-def456" -d '<payload>'
  2. Response: HTTP 599 from the sink (the request was forwarded). Sink capture of the outbound to aiplatform.googleapis.com: authorization: Bearer ya29.fake-google-oauth only. x-litellm-api-key, api-key, and x-api-key are all <absent>, and neither azure-secret-abc123, anthropic-secret-def456, nor the virtual key appears anywhere. The Google token still forwards, every proxy auth header is dropped

Case G: virtual key in the operator-configured custom key header

Against the proxy configured with general_settings.litellm_key_header_name: x-company-key

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41779/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-company-key: Bearer sk-…" -H "x-goog-api-key: AIzaSyReal-Google-Api-Key-000" -d '<payload>'
  2. Response: HTTP 599 from the sink (the request was forwarded). Sink capture of the outbound to aiplatform.googleapis.com: x-goog-api-key: AIzaSyReal-Google-Api-Key-000 only, x-company-key: <absent>, and the virtual key is nowhere in the outbound. The caller still authenticates with the custom header, the real Google key still forwards, and the virtual key is stripped
  3. Sending the virtual key in x-company-key with no real Google credential returns HTTP 401 and nothing reaches googleapis.com

Case H: caller Azure APIM secret in Ocp-Apim-Subscription-Key, legitimate Google header preserved

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:41337/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Bearer ya29.fake-google-oauth" -H "Ocp-Apim-Subscription-Key: azure-apim-secret-xyz789" -H "X-Goog-User-Project: my-billing-proj" -d '<payload>'
  2. Response: HTTP 599 from the sink (the request was forwarded). Sink capture of the outbound to aiplatform.googleapis.com: authorization: Bearer ya29.fake-google-oauth and X-Goog-User-Project: my-billing-proj, while Ocp-Apim-Subscription-Key is <absent> and azure-apim-secret-xyz789 appears nowhere. The caller's Azure APIM secret is dropped, and the genuine Google X-Goog-User-Project header is preserved so real Vertex requests keep working

Case I: virtual key resolved through the full auth precedence, and no regression to real credentials

The route authenticates through Depends(user_api_key_auth), which accepts the caller key from every header in SpecialHeaders.litellm_credential_header_names(), x-goog-api-key included. The filter now resolves the caller key by that same precedence and value-strips exactly the value that authenticated, so x-goog-api-key is stripped when it carried the key and preserved when it carried a real Google key alongside a higher-precedence virtual key. All against the port 40923 proxy at this tip:

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:40923/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-goog-api-key: sk-…" -d '<payload>' returns HTTP 401 and the sink records nothing new (the proxy's own auth rejects a virtual key in x-goog-api-key on this route today, and the filter would strip it regardless, closing the boundary that a mocked unit test exercises directly)
  2. Case C re-run (x-litellm-api-key: sk-… + Authorization: Bearer ya29.fake-google-oauth) still returns HTTP 599 with authorization: Bearer ya29.fake-google-oauth forwarded and the virtual key absent
  3. Case E re-run (x-litellm-api-key: sk-… + x-goog-api-key: AIzaSyReal-Google-Api-Key-000) still returns HTTP 599 with x-goog-api-key: AIzaSyReal-Google-Api-Key-000 forwarded and the virtual key absent, so resolving by precedence does not strip a genuine Google key

Case J: virtual key echoed into Authorization with a Basic scheme, and no regression to real credentials

Against a fresh default-config proxy on port 40611 at this tip:

  1. curl -sw '%{http_code}' -X POST http://127.0.0.1:40611/vertex_ai/.../gemini-2.5-pro:generateContent -H "x-litellm-api-key: sk-…" -H "Authorization: Basic sk-…" -d '<payload>' returns HTTP 401 and the sink records nothing new. Normalizing the value with the auth module's own _get_bearer_token strips the Basic scheme just as authentication does, so the echoed virtual key matches the authenticated key and Authorization is dropped, leaving no upstream credential
  2. Case C re-run (x-litellm-api-key: sk-… + Authorization: Bearer ya29.fake-google-oauth) still returns HTTP 599 with authorization: Bearer ya29.fake-google-oauth forwarded and the virtual key absent
  3. Case E re-run (x-litellm-api-key: sk-… + x-goog-api-key: AIzaSyReal-Google-Api-Key-000) still returns HTTP 599 with x-goog-api-key: AIzaSyReal-Google-Api-Key-000 forwarded and the virtual key absent

Case K: virtual key in the mapped-route litellm_user_api_key header, with real credentials preserved

The /vertex_ai prefix is a mapped pass-through route, so user_api_key_auth accepts the caller key from a header literally named litellm_user_api_key and applies it last, overriding every other source. Against fresh default-config proxies on ports 40611 (before this commit) and 40337 (this tip), same request each side: litellm_user_api_key: sk-… (auth) + Authorization: Bearer ya29.fake-google-oauth + x-goog-api-key: AIzaSyReal-Google-Api-Key-000.

  1. Before (port 40611): HTTP 599, and the sink capture to aiplatform.googleapis.com shows litellm_user_api_key: sk-… forwarded with the virtual key, while the real Authorization was dropped. The virtual key reached Google and the bring-your-own token was lost
  2. After (port 40337): HTTP 599, and the sink capture shows authorization: Bearer ya29.fake-google-oauth and x-goog-api-key: AIzaSyReal-Google-Api-Key-000 both forwarded, litellm_user_api_key <absent>, and the virtual key nowhere. The key that authenticated is stripped, and both real credentials are preserved

Type

🐛 Bug Fix

Caveats (if any)

Scope of this PR is the credential-less Vertex passthrough leaking the caller's LiteLLM credential through request headers. One adjacent vector is intentionally left for a separate, focused change: a caller can also send a virtual key in the ?key= URL query parameter (the Google AI Studio auth convention that user_api_key_auth reads on generateContent routes). A client that authenticates only with ?key= is already rejected with a 401 on this branch (no surviving upstream Google credential), so that common case does not leak. The virtual key does still ride the forwarded URL query when the caller both authenticates with a header and brings a real Google credential, but stripping credential query params belongs in the shared pass-through URL-forwarding path (it affects every provider, not just Vertex) and is being tracked as its own follow-up rather than widening this PR's blast radius.

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Live PR risk

/live-pr-risk CHECKED e6eb6a4: SAFE. The graph is tiny and fully local: _forwarded_headers_for_credentialless_vertex_passthrough is called only by _prepare_vertex_auth_headers, which is called only by _base_vertex_proxy_route, which is reached by the two public routes vertex_proxy_route and vertex_discovery_proxy_route. The re-signed _prepare_vertex_auth_headers now returns Mapping[str, str] and its credential-less branch raises HTTPException(401); nothing downstream mutates the returned headers, and create_pass_through_route copies them with dict(...), so the immutable MappingProxyType is safe on every path. vertex_proxy_route was driven live in the proof above; vertex_discovery_proxy_route runs the identical credential-less branch and is covered by unit tests. Direct-caller and related passthrough tests pass on the fixed head (160 passed), including test_vertex_passthrough_load_balancing.py, which unpacks the new tuple directly.

/live-pr-risk CHECKED 4bc0977: SAFE. This commit hardens the same branch to strip the virtual key from every forwarded header by value (normalizing any Bearer prefix) instead of only from Authorization by name, closing the x-goog-api-key vector Greptile flagged. Re-walked the graph for the added code: the new _bearer_stripped is a pure module-private helper referenced only inside _forwarded_headers_for_credentialless_vertex_passthrough, and that function still has exactly one caller, so the blast radius is identical to the commit above. No new dependents, side effects, or raises.

/live-pr-risk CHECKED e7c2ede: SAFE. This commit additionally drops the proxy-only auth headers Google never consumes (x-litellm-api-key, api-key, x-api-key) by name via a new module-private _HEADERS_NEVER_FORWARDED_TO_VERTEX frozenset, keeping the by-value virtual-key strip for Authorization / x-goog-api-key. The frozenset is referenced only inside _forwarded_headers_for_credentialless_vertex_passthrough, which still has exactly one caller, so the blast radius is unchanged; no new dependents, side effects, or raises, and the gate is untouched.

/live-pr-risk CHECKED ee03632: SAFE. This commit closes the last vector Greptile flagged: user_api_key_auth also authenticates a caller from the operator-configured general_settings.litellm_key_header_name, read straight off the request, so a virtual key sent there survived the filter. The new module-private _credentialless_caller_key_values reads general_settings (read-only, via the same lazy import already used elsewhere in the module) and returns the set of accepted key values; the filter now value-strips any header matching one of them. Blast radius unchanged: both the helper and the filter are called only from the single existing caller, no new raises (the 401 gate is untouched), no mutation, no signature change. Verified live on both sides: at the merge-base with litellm_key_header_name: x-company-key set, x-company-key: Bearer <vkey> forwarded to aiplatform.googleapis.com alongside a real Google key; on this tip the same request forwards only the real Google key with the custom header and the virtual key stripped, and the custom header alone returns 401. Cases A-F re-verified unchanged on this tip.

/live-pr-risk CHECKED ab93636: SAFE. This commit replaces the hand-rolled name-drop set with one derived from the canonical SpecialHeaders.litellm_credential_header_names() minus the two headers that double as real Google credentials (Authorization, x-goog-api-key), which are value-stripped instead. This is the same source user_api_key_auth reads the caller's key from, so the drop set now cannot drift out of sync with what authenticates, and it picked up Ocp-Apim-Subscription-Key, which the hand-rolled set missed. SpecialHeaders is a pure enum already imported into the module via its _types star import; the derived frozenset is evaluated once at import with no side effects, and both module-level constants are referenced only inside the single-caller filter, so the blast radius is unchanged. Verified live on both sides: at the merge-base a distinct caller Azure APIM secret in Ocp-Apim-Subscription-Key forwarded to aiplatform.googleapis.com; on this tip it is dropped while the genuine Google X-Goog-User-Project header is preserved, and all of Cases A-G re-verified unchanged.

/live-pr-risk CHECKED f3dc339: SAFE. This commit resolves the caller key by the same precedence get_api_key uses (custom litellm_key_header_name, then x-litellm-api-key, Authorization, api-key, x-api-key, x-goog-api-key, Ocp-Apim-Subscription-Key) and value-strips exactly the one value that authenticated, instead of only the value from x-litellm-api-key / Authorization / the custom header. This closes the structural gap Greptile flagged: x-goog-api-key is an accepted auth source that the old caller-key set omitted while keeping the header, so a virtual key authenticated through it would have been forwarded. The precedence tuple is built once at import from the same SpecialHeaders enum; _authenticated_caller_key_values reads request headers and general_settings read-only and is still called only by the single-caller filter, so the blast radius is unchanged, no new raises, no mutation. Verified live at this tip: a real Google key in x-goog-api-key alongside a higher-precedence virtual key in x-litellm-api-key is still forwarded (Case E, 599), the bring-your-own OAuth token is still forwarded (Case C, 599), and a virtual key sent only in x-goog-api-key is rejected. Note the proxy's own auth currently rejects a virtual key presented in x-goog-api-key on this route before the filter runs, so this commit is defense-in-depth on the forwarding boundary, proven directly by the added unit tests.

/live-pr-risk CHECKED 5d8286c: SAFE. This commit swaps the filter's own Bearer-only stripping for the auth module's _get_bearer_token, so the caller-key comparison normalizes exactly the schemes authentication accepts (Bearer / bearer / Basic / AWS4-HMAC-SHA256), with a raw-value fallback for a bare token. It closes the case Greptile flagged: a virtual key echoed as Authorization: Basic <key> alongside a higher-precedence auth header did not match the caller key under the old normalization and was forwarded. _get_bearer_token is a pure function in user_api_key_auth (already imported into this module for user_api_key_auth), with no side effects; the new _normalize_credential_value wrapper is referenced only by the single-caller resolver and filter, so the blast radius is unchanged. Verified live on both sides: at the merge-base Authorization: Basic <vkey> forwarded to aiplatform.googleapis.com; on this tip it returns 401 with nothing forwarded, while the bring-your-own OAuth token (Case C) and a real Google key (Case E) still forward with the virtual key absent.

/live-pr-risk CHECKED 2fe1e7e: SAFE. user_api_key_auth also accepts the caller key from a pass_through_endpoints entry's headers.litellm_user_api_key, not only litellm_key_header_name. This commit adds _operator_configured_caller_key_header_names, which reads both from general_settings (read-only), drops every configured caller-key header by name, and feeds them as top-precedence caller-key sources into the resolver. The helper is pure and referenced only by the single-caller resolver and filter, so the blast radius is unchanged, no new raises, no mutation. Config values are read defensively with isinstance guards. Covered by unit tests that configure each source through general_settings and assert the configured header is dropped while a real Google key in x-goog-api-key is preserved; the full passthrough test file (160 tests) passes, including the LIT-4761 streaming-classification suite whose fixture now sends the virtual key in x-litellm-api-key, matching a real request.

/live-pr-risk CHECKED fcc047b: SAFE. Corrects the precedence of the operator-configured key headers to match get_api_key exactly: litellm_key_header_name overrides everything so it resolves first, then the built-in headers in get_api_key order, then a pass_through_endpoints litellm_user_api_key header which get_api_key checks last. The prior commit had lumped both configured sources at the top, so a request that authenticated via Authorization while also carrying a pass-through header could pick the wrong value and leave the authenticated Authorization key forwarded. _operator_configured_caller_key_header_names now returns (override, pass_through) and both the resolver ordering and the name-drop consume it; still pure, still called only by the single-caller resolver and filter, no new raises or mutation. Covered by a new unit test where Authorization holds the authenticated key, a pass-through header holds a decoy, and a real x-goog-api-key is present: the Authorization key is stripped, the pass-through header dropped, and the real Google key preserved. Full passthrough test file (161 tests) green.

/live-pr-risk CHECKED 16a81c9: SAFE. Closes a high-severity vector Cursor Bugbot flagged: on mapped pass-through routes (of which /vertex_ai is one), user_api_key_auth accepts the caller key from a header literally named litellm_user_api_key via check_api_key_for_custom_headers_or_pass_through_endpoints, applied last so it overrides every other source. The filter now drops that header by name and resolves it at highest precedence. Adds a module constant plus a prepend to the resolver order and a union into the name-drop set; still pure, still called only by the single-caller resolver and filter, no new raises or mutation. Verified live on both sides: at the previous tip a virtual key in litellm_user_api_key forwarded to aiplatform.googleapis.com while the real Authorization was wrongly stripped; on this tip the virtual key is dropped and both the bring-your-own OAuth token and a real Google key are preserved. Full passthrough test file (163 tests) green.


Note

High Risk
Security-sensitive change to Vertex passthrough auth and header forwarding; wrong filtering could break BYO-Google flows or still leak secrets upstream.

Overview
Fixes LIT-5997: when the proxy has no Vertex credential, the bring-your-own-credentials branch no longer forwards the full incoming header set to Google.

Credential-less Vertex passthrough now builds upstream headers via _forwarded_headers_for_credentialless_vertex_passthrough instead of copying all request headers. Proxy-only auth headers (from SpecialHeaders.litellm_credential_header_names() except Authorization / x-goog-api-key, plus operator litellm_key_header_name and pass-through key headers) are dropped by name. The caller key is resolved with the same precedence as user_api_key_auth and stripped by value from any remaining header (using _get_bearer_token for scheme normalization). If neither a surviving Authorization nor x-goog-api-key remains, the route returns 401 with guidance instead of calling Google.

Bring-your-own Google OAuth or API keys still forward; LiteLLM virtual keys in Authorization, x-litellm-api-key, x-goog-api-key, or custom headers no longer reach upstream. Tests were updated and expanded (TestVertexCredentiallessPassthroughVirtualKeyLeak) for these cases.

Reviewed by Cursor Bugbot for commit 16a81c9. Bugbot is set up for automated code reviews on this repo. Configure here.

…-less Vertex passthrough

When no Vertex credential is configured (no default_vertex_config, no matching
use_in_pass_through deployment, no vector-store credential), the Vertex passthrough
took the bring-your-own-credentials branch and forwarded the entire incoming header
set upstream to Google. That set included whichever header carried the caller's
LiteLLM virtual key: x-litellm-api-key, or Authorization when get_litellm_virtual_key
read the key from there. The proxy's own secret was sent to a third-party provider.

The credential-less branch now drops x-litellm-api-key and the Authorization value
that equals the virtual key, keeping a genuine bring-your-own Google credential
(an OAuth token in Authorization, or x-goog-api-key) so real BYO passthrough still
works. When neither survives, the request fails with a clean 401 telling the operator
no credential is configured, instead of forwarding the virtual key.

Regression coverage in the mapped test path asserts the 401-and-never-forwarded
behavior for both leak vectors and that a real Google credential still passes through
with the virtual key stripped.
@codecov

codecov Bot commented Aug 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.50000% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ass_through_endpoints/llm_passthrough_endpoints.py 62.50% 15 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR hardens credential-less Vertex passthrough handling and rejects requests that lack a usable upstream credential

  • Derives proxy-only credential headers from the canonical authentication-header set
  • Matches caller-key precedence across built-in, mapped, and operator-configured headers
  • Preserves distinct bring-your-own Google credentials while removing proxy credentials
  • Adds focused regression coverage for the previously reported header and normalization cases

Confidence Score: 5/5

The PR appears safe to merge

No blocking failure remains

Important Files Changed

Filename Overview
litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Adds credential-aware filtering and an early rejection path; the fixes address all displayed prior findings
tests/test_litellm/proxy/pass_through_endpoints/test_llm_pass_through_endpoints.py Adds regression tests for credential source precedence, configured headers, supported schemes, and bring-your-own credentials

Reviews (11): Last reviewed commit: "fix(vertex-passthrough): cover the mappe..." | Re-trigger Greptile

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
…ss Vertex forward

The credential-less Vertex passthrough dropped the caller's LiteLLM
virtual key only from Authorization by exact match. A caller who sent
the same key in x-goog-api-key (which doubles as a real Google
credential) had it accepted as a credential and forwarded upstream.

Drop the virtual key by value across every forwarded header, normalizing
any Bearer prefix, so no header name carries it to Google.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
Adds a regression asserting the value-based strip also drops the caller's
virtual key when it is duplicated into the api-key and x-api-key headers,
while a genuine bring-your-own Google credential still forwards.
On the credential-less Vertex passthrough branch, drop every header that
can only carry LiteLLM caller auth (x-litellm-api-key, api-key, x-api-key)
by name, since Google never consumes them, and strip the virtual key by
value from Authorization / x-goog-api-key, which may instead hold a genuine
bring-your-own Google credential. This closes the residual leak where a
distinct caller secret in api-key or x-api-key still reached upstream.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
@veria-ai

veria-ai Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 2 · PR risk: 0/10

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
user_api_key_auth also authenticates a caller from the operator-configured
general_settings.litellm_key_header_name, reading that header straight off
the request, so a virtual key sent there survived the credential-less Vertex
forwarding filter and reached Google alongside a real bring-your-own
credential. Value-strip every header whose value matches the caller's key
from any accepted source, including that custom header.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
…alHeaders

The hand-rolled drop set missed Ocp-Apim-Subscription-Key, so a caller
Azure APIM secret in that header was forwarded to Google on the
credential-less branch. Derive the name-drop set from the canonical
SpecialHeaders.litellm_credential_header_names(), minus Authorization and
x-goog-api-key which double as real Google credentials and are value-stripped
instead. New credential headers added there are now dropped automatically.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
The credential-less filter derived the caller key only from x-litellm-api-key,
Authorization, and the custom header, but the route authenticates through
Depends(user_api_key_auth), which also accepts the key from x-goog-api-key. A
virtual key sent only in x-goog-api-key therefore authenticated yet was kept as
a preserved upstream header and forwarded to Google. Resolve the caller key by
the same precedence get_api_key uses and value-strip exactly that, so a key in
x-goog-api-key is stripped while a real Google key alongside a higher-precedence
virtual key is preserved.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
…er_token

The filter's own Bearer-only stripping missed the other schemes
user_api_key_auth accepts, so a virtual key echoed as `Authorization: Basic
<key>` alongside a higher-precedence auth header did not match the caller key
and was forwarded to Google. Reuse the auth module's _get_bearer_token so the
comparison strips exactly what authentication does (Bearer / bearer / Basic /
AWS4-HMAC-SHA256), falling back to the raw value for a bare token.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

…in streaming tests

The LIT-4761 streaming-classification tests passed only the bring-your-own
Google OAuth token in Authorization and mocked get_litellm_virtual_key, a shape
that cannot authenticate in production. The credential-less filter now resolves
the caller key by auth precedence, so a lone Authorization value reads as the
key and is stripped. Send the virtual key in x-litellm-api-key, matching a real
request, so Authorization is preserved and the classification assertions run.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
…eaders

user_api_key_auth also accepts the caller key from a pass_through_endpoints
entry's headers.litellm_user_api_key, not just litellm_key_header_name. Drop
every operator-configured caller-key header by name and treat them as
top-precedence caller-key sources, so a virtual key sent through one is never
forwarded to Google.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py Outdated
…key headers

The resolver placed both operator-configured key headers at the top of its
precedence, but user_api_key_auth only overrides with litellm_key_header_name;
a pass_through_endpoints litellm_user_api_key is checked last. So a request that
authenticated via Authorization while also sending a pass-through header could
have the wrong value chosen, leaving the authenticated Authorization key
forwarded. Order the resolver exactly like get_api_key: override first, built-in
headers next, pass-through header last.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py
…header

On mapped pass-through routes, of which /vertex_ai is one,
user_api_key_auth accepts the caller key from a header literally named
litellm_user_api_key and applies it last, so it overrides every other source.
The credential-less filter neither dropped it nor resolved the caller key from
it, so a virtual key there reached Google past a real x-goog-api-key, and a
bring-your-own Authorization could be stripped when auth actually came from that
header. Drop it by name and resolve it at highest precedence.
@mateo-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@mateo-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 16a81c9. Configure here.

@tin-berri tin-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — real leak, right fix. Forwarding _safe_get_request_headers(request).copy() wholesale meant the caller's virtual key went to Google on every credential-less Vertex passthrough, and splitting it into "drop the proxy-only auth headers by name, drop the value that actually authenticated wherever it appears, 401 if nothing usable survives" is the correct decomposition. Reusing _get_bearer_token from user_api_key_auth rather than re-deriving the scheme stripping is exactly right — that comparison has to agree with authentication or it strips the wrong thing.

Traced the cases that decide whether this is correct or just looks correct:

  • virtual key in Authorization only → stripped by value, nothing survives, 401 with the actionable message. Right.
  • virtual key in x-litellm-api-key + real Google token in Authorization → key dropped by name, Authorization value ≠ caller key so it's preserved. Right.
  • virtual key in x-goog-api-key → resolved as the authenticated value and dropped by value, not kept just because Google consumes that header. This is the case a by-name-only fix would have missed.

Also checked the dict → Mapping return-type change for runtime breakage: create_pass_through_route does dict(param_custom_headers) at the pass_through_request call, and forward_headers_from_request rebinds ({**request_headers, **headers}) rather than mutating in place, so the MappingProxyType never reaches anything that writes to it.

One thing worth a guard. The whole fix rests on forward_headers being False on this route, and nothing here says so. If it's ever True, forward_headers_from_request merges the raw incoming headers back in — and it only pops a request header when that name is already present in headers, so every header this PR dropped by name (x-litellm-api-key, api-key, x-api-key, Ocp-Apim-Subscription-Key, litellm_user_api_key, the operator-configured ones) gets re-added verbatim and the leak is back. Today _base_vertex_proxy_route doesn't pass _forward_headers and the default is False, so it's safe — but that's an invisible dependency holding up a security fix. Either assert it at the call site or note it where _forward_headers_for_credentialless_vertex_passthrough is defined, so someone enabling header forwarding on this route trips over it.

code-quality is the recursive_detector red on llm_request_utils.py — base drift, and #38149 fixes it.

@mateo-berri
mateo-berri merged commit 46c2328 into litellm_internal_staging Aug 24, 2026
80 of 82 checks passed
@mateo-berri
mateo-berri deleted the litellm_fix_5997_vertex_pt_key_leak branch August 24, 2026 21:54
@codspeed

codspeed Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_fix_5997_vertex_pt_key_leak (16a81c9) with litellm_internal_staging (a91cac7)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (6147b3c) during the generation of this report, so a91cac7 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

mateo-berri added a commit to FelipeRodriguesGare/litellm that referenced this pull request Aug 26, 2026
…ertex passthrough

PR BerriAI#38114 dropped whichever header user_api_key_auth would read the caller's
key from, by precedence. Under custom_auth, JWT auth, or no master key that
header is the caller's own Google token, so the bring-your-own-credentials
Vertex branch answered 401 to every valid request.

A header value is now dropped only when it is the master key or when its
hash is the api_key that authenticated the request, so a Google token that
auth never consumed keeps flowing while a LiteLLM key still never reaches
Google.

test_passthrough_post_call_guardrails.py no longer plants a MagicMock
proxy_server module in sys.modules at import, which poisoned sibling tests
that read module globals at call time.
doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request Sep 6, 2026
…00.0) (#124)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/berriai/litellm](https://images.chainguard.dev/directory/image/wolfi-base/overview) ([source](https://github.com/BerriAI/litellm)) | minor | `v1.99.1` → `v1.100.0` |

---

### Release Notes

<details>
<summary>BerriAI/litellm (ghcr.io/berriai/litellm)</summary>

### [`v1.100.0`](https://github.com/BerriAI/litellm/releases/tag/v1.100.0)

[Compare Source](https://github.com/BerriAI/litellm/compare/v1.99.1...v1.100.0)

#### Verify Docker Image Signature

All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit `0112e53`](https://github.com/BerriAI/litellm/commit/0112e53046018d726492c814b3644b7d376029d0).

**Verify using the pinned commit hash (recommended):**

A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key:

```bash
cosign verify \
  --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \
  ghcr.io/berriai/litellm:v1.100.0
```

**Verify using the release tag (convenience):**

Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules:

```bash
cosign verify \
  --key https://raw.githubusercontent.com/BerriAI/litellm/v1.100.0/cosign.pub \
  ghcr.io/berriai/litellm:v1.100.0
```

Expected output:

```
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key
```

***

#### What's Changed

- fix(responses): keep the conversation when chaining previous\_response\_id on the bridge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;37956](https://github.com/BerriAI/litellm/pull/37956)
- feat(newrelic): per-team New Relic trace routing via team callbacks by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;37603](https://github.com/BerriAI/litellm/pull/37603)
- perf(ci): cache uv dependencies in the lint job by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37783](https://github.com/BerriAI/litellm/pull/37783)
- perf(ci): fan the budget checkers out across cores by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37784](https://github.com/BerriAI/litellm/pull/37784)
- ci: port the Postgres suites off CircleCI onto service containers by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37785](https://github.com/BerriAI/litellm/pull/37785)
- feat(ci): gate patching of SDK internals in tests as TQ008 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37787](https://github.com/BerriAI/litellm/pull/37787)
- ci: measure enterprise/ coverage by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37788](https://github.com/BerriAI/litellm/pull/37788)
- ci: run the keyless caching tests that ran in no job by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37790](https://github.com/BerriAI/litellm/pull/37790)
- fix(ci): run the migration DDL guard, and stop it reading comments as SQL by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37791](https://github.com/BerriAI/litellm/pull/37791)
- ci: run the enterprise package suite in GitHub Actions by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37798](https://github.com/BerriAI/litellm/pull/37798)
- perf(ci): give the two longest unit shards the runner's spare cores by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37804](https://github.com/BerriAI/litellm/pull/37804)
- test(exception-mapping): pin the status and error-shape table every provider maps to by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37807](https://github.com/BerriAI/litellm/pull/37807)
- fix(terraform): add soft\_budget, tags, and soft\_budget\_alerting\_emails to litellm\_team by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37918](https://github.com/BerriAI/litellm/pull/37918)
- fix(ui): theme the created-key box so it follows dark mode by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37985](https://github.com/BerriAI/litellm/pull/37985)
- fix(ui): restore the public model name tooltip layout in the add model flow by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37986](https://github.com/BerriAI/litellm/pull/37986)
- fix(ui): render team and org tpm/rpm limits of 0 as 0 instead of Unlimited by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37916](https://github.com/BerriAI/litellm/pull/37916)
- fix(ui): repoint the key detail URL to the rotated hash after regenerating by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37968](https://github.com/BerriAI/litellm/pull/37968)
- fix(ui): make playground chat bubbles theme-aware by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;37978](https://github.com/BerriAI/litellm/pull/37978)
- fix(UI): correct skill install command and marketplace setup UX by [@&#8203;ozolam](https://github.com/ozolam) in [#&#8203;33514](https://github.com/BerriAI/litellm/pull/33514)
- fix(proxy): skip health checks for strategy routers by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37966](https://github.com/BerriAI/litellm/pull/37966)
- fix(databricks): bill cached tokens at cache rates and add missing Claude pricing by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;37975](https://github.com/BerriAI/litellm/pull/37975)
- fix(anthropic): round-trip thinking blocks to OpenAI backends on /v1/messages by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;37953](https://github.com/BerriAI/litellm/pull/37953)
- fix(a2a): normalize agent card protocolBinding casing before transport match by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37917](https://github.com/BerriAI/litellm/pull/37917)
- fix(interactions): track cost and spend for Google Interactions API requests by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;33310](https://github.com/BerriAI/litellm/pull/33310)
- fix(bedrock): stop emitting an empty assistant delta after the finish\_reason chunk by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36806](https://github.com/BerriAI/litellm/pull/36806)
- fix(anthropic): reconcile enum with declared type in output\_format schema by [@&#8203;dkindlund](https://github.com/dkindlund) in [#&#8203;37882](https://github.com/BerriAI/litellm/pull/37882)
- feat(azure\_ai): support entra id / oauth auth on every azure ai foundry route by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35415](https://github.com/BerriAI/litellm/pull/35415)
- fix(ui): boot the UI image as an arbitrary uid by anchoring nginx writes under /tmp by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37982](https://github.com/BerriAI/litellm/pull/37982)
- fix(proxy): parse form-encoded video edit/extension bodies after auth by [@&#8203;Souravrajvi0](https://github.com/Souravrajvi0) in [#&#8203;36513](https://github.com/BerriAI/litellm/pull/36513)
- fix(anthropic): keep legacy thinking budget\_tokens on Claude 4.6 models on /v1/messages by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38108](https://github.com/BerriAI/litellm/pull/38108)
- fix(utils): make prompt\_token\_calculator count claude models again by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38130](https://github.com/BerriAI/litellm/pull/38130)
- fix(proxy): keep every value of a repeated form key, and gate the tests that hid it by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;37908](https://github.com/BerriAI/litellm/pull/37908)
- fix(health): apply model\_info.health\_check\_params to health check probes by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38101](https://github.com/BerriAI/litellm/pull/38101)
- fix(runwayml): route every generation endpoint and fix video cost tracking by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38115](https://github.com/BerriAI/litellm/pull/38115)
- fix(passthrough): attribute spend and release budget reservation on router-model /vllm and /azure routes by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38111](https://github.com/BerriAI/litellm/pull/38111)
- fix: match OpenAI SDK wire format on image/video routes by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38104](https://github.com/BerriAI/litellm/pull/38104)
- fix(ci): give three unit shards a job deadline that outlasts their pytest budget by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38139](https://github.com/BerriAI/litellm/pull/38139)
- feat(ui): add Gemini Family auto-router preset by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38138](https://github.com/BerriAI/litellm/pull/38138)
- fix(logging\_worker): carry queued tasks across event-loop change instead of dropping them by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38144](https://github.com/BerriAI/litellm/pull/38144)
- feat(proxy): enforce vector-store upload security controls on /v1/rag/ingest by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38135](https://github.com/BerriAI/litellm/pull/38135)
- test(e2e): pin require\_managed\_files enforcement behind a marker-gated stack phase by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38117](https://github.com/BerriAI/litellm/pull/38117)
- refactor(ui): move the dashboard onto class-variance-authority by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38125](https://github.com/BerriAI/litellm/pull/38125)
- refactor(utils)!: delete prompt\_token\_calculator by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38132](https://github.com/BerriAI/litellm/pull/38132)
- fix(auto-router): list configured auto-routers in the usage picker before they have traffic by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38129](https://github.com/BerriAI/litellm/pull/38129)
- refactor(ui): install the shadcn field primitive by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38126](https://github.com/BerriAI/litellm/pull/38126)
- fix(complexity\_router): keep both ends of a clipped classifier context turn by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38141](https://github.com/BerriAI/litellm/pull/38141)
- fix(ci): ignore-list recursive form-field flatteners in recursive\_detector by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38149](https://github.com/BerriAI/litellm/pull/38149)
- fix(passthrough): stop leaking the caller's virtual key on credential-less Vertex passthrough by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38114](https://github.com/BerriAI/litellm/pull/38114)
- fix(router): stop copying forwarded credentials into retry breadcrumbs by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38133](https://github.com/BerriAI/litellm/pull/38133)
- feat(e2e): record and replay streamed provider responses chunk-for-chunk by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38136](https://github.com/BerriAI/litellm/pull/38136)
- fix: tolerate stream chunks without a choices key in stream\_chunk\_builder by [@&#8203;AkshaySasi](https://github.com/AkshaySasi) in [#&#8203;34382](https://github.com/BerriAI/litellm/pull/34382)
- fix(files): decode x-litellm-model encoded file\_id in chat + responses by [@&#8203;hclsys](https://github.com/hclsys) in [#&#8203;29832](https://github.com/BerriAI/litellm/pull/29832)
- fix(videos): forward uploaded source file on /v1/videos/edits to the provider by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38155](https://github.com/BerriAI/litellm/pull/38155)
- fix(s3\_v2): percent-encode object keys once so signed and sent URLs match by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38005](https://github.com/BerriAI/litellm/pull/38005)
- feat(ui): add error-code drilldown for failed requests on caching page by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38156](https://github.com/BerriAI/litellm/pull/38156)
- feat(search): add Grounding with Bing Search (bing\_grounding) as a search provider by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38119](https://github.com/BerriAI/litellm/pull/38119)
- ci: ban row-rewriting DML from prisma migrations by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;37899](https://github.com/BerriAI/litellm/pull/37899)
- fix(langsmith): keep root-run ids self-consistent so batch ingest stops rejecting header-tagged requests by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38116](https://github.com/BerriAI/litellm/pull/38116)
- ci(e2e): record the e2e suite weekly and replay it on weekdays with zero egress by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38163](https://github.com/BerriAI/litellm/pull/38163)
- chore(codeowners): unown ui container plumbing and generated files by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38124](https://github.com/BerriAI/litellm/pull/38124)
- fix(logging): skip parsing redacted tool call arguments by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38169](https://github.com/BerriAI/litellm/pull/38169)
- feat(complexity\_router): bound the classifier context block, not each turn in it by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38145](https://github.com/BerriAI/litellm/pull/38145)
- fix(http\_handler): dispose aiohttp session when AsyncHTTPHandler is finalized without a running loop by [@&#8203;anmolg1997](https://github.com/anmolg1997) in [#&#8203;36670](https://github.com/BerriAI/litellm/pull/36670)
- fix(proxy): reset a stuck team member's budget by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;37971](https://github.com/BerriAI/litellm/pull/37971)
- fix(anthropic/bedrock): request summarized adaptive thinking for reasoning\_effort and use provider thinking token counts by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37979](https://github.com/BerriAI/litellm/pull/37979)
- fix(completion\_extras): forward reasoning\_effort=max through the Responses API bridge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38222](https://github.com/BerriAI/litellm/pull/38222)
- feat(vertex\_ai): add native Vertex AI Interactions API support by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38229](https://github.com/BerriAI/litellm/pull/38229)
- test(mcp): drain the logging worker after each test so queued callbacks cannot leak into the next test by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38228](https://github.com/BerriAI/litellm/pull/38228)
- fix(ui): forward OAuth issuer/authorization/token/registration URLs from the MCP server edit form by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;38154](https://github.com/BerriAI/litellm/pull/38154)
- fix(together\_ai): default endpoints to api.together.ai instead of api.together.xyz by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38233](https://github.com/BerriAI/litellm/pull/38233)
- fix(bedrock\_mantle): register a Bedrock runtime passthrough config so /bedrock/model/<deployment>/invoke works by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38231](https://github.com/BerriAI/litellm/pull/38231)
- fix(router): resolve provider from api\_base in deployment validation and acompletion by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38235](https://github.com/BerriAI/litellm/pull/38235)
- fix(model\_prices): raise bedrock\_mantle gpt-5.6 max\_input\_tokens to Mantle's enforced [`1050000`](https://github.com/BerriAI/litellm/commit/1050000) by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38225](https://github.com/BerriAI/litellm/pull/38225)
- fix(bedrock\_mantle): normalize Codex input item types Mantle rejects by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38227](https://github.com/BerriAI/litellm/pull/38227)
- fix(proxy): store the actual selected model in spend logs for Azure Model Router by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37770](https://github.com/BerriAI/litellm/pull/37770)
- feat(router): per-group supported reasoning efforts with the max level by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;37897](https://github.com/BerriAI/litellm/pull/37897)
- fix(proxy): stop expected 4xx responses from saturating worker CPU on failure logging by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38102](https://github.com/BerriAI/litellm/pull/38102)
- fix(caching): use upstream RedisCluster on redis-py with per-connection recovery by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38171](https://github.com/BerriAI/litellm/pull/38171)
- perf(auth): drop guaranteed-miss internal-cache Redis read from team object lookup by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38073](https://github.com/BerriAI/litellm/pull/38073)
- fix(together\_ai): route chat completions through a dedicated TogetherAIChatConfig by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38248](https://github.com/BerriAI/litellm/pull/38248)
- fix(ui): read reasoning tokens from Responses API output\_tokens\_details by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;37952](https://github.com/BerriAI/litellm/pull/37952)
- fix(dashboard): don't show a stale provider prompt-cache chip on a response-cache hit by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;37951](https://github.com/BerriAI/litellm/pull/37951)
- fix(ui): render tag-based guardrail mode instead of crashing the guardrails page by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37493](https://github.com/BerriAI/litellm/pull/37493)
- fix(scim): return user\_id as Group members\[].value on transformed group responses by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38161](https://github.com/BerriAI/litellm/pull/38161)
- fix(scim): preserve existing team memberships when POST /Users adoption carries no groups by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38166](https://github.com/BerriAI/litellm/pull/38166)
- fix(router): support mid-stream fallback for anthropic\_messages route type by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;38153](https://github.com/BerriAI/litellm/pull/38153)
- fix(auth): support wildcard prefixes in jwt team\_allowed\_routes by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37756](https://github.com/BerriAI/litellm/pull/37756)
- feat(models): add missing Together AI serverless models to the cost map by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38230](https://github.com/BerriAI/litellm/pull/38230)
- fix(cerebras)!: add max\_retries and extra\_headers to get\_supported\_openai\_params by [@&#8203;deepanshululla](https://github.com/deepanshululla) in [#&#8203;36601](https://github.com/BerriAI/litellm/pull/36601)
- fix(anthropic): translate tool\_result document blocks in the /v1/messages bridge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38251](https://github.com/BerriAI/litellm/pull/38251)
- fix(team): serialize member\_add, member\_delete, and delete under the team's advisory lock by [@&#8203;yassin-berriai](https://github.com/yassin-berriai) in [#&#8203;37969](https://github.com/BerriAI/litellm/pull/37969)
- docs(pr-template): split Caveats bullets into severity tiers and call for plain engineering language by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38252](https://github.com/BerriAI/litellm/pull/38252)
- fix(anthropic): carry tool\_result document blocks through the /v1/messages responses bridge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38261](https://github.com/BerriAI/litellm/pull/38261)
- fix(together\_ai): pass tools through for models missing from the registry by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38265](https://github.com/BerriAI/litellm/pull/38265)
- fix(anthropic): carry user-content document blocks through the /v1/messages responses bridge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38267](https://github.com/BerriAI/litellm/pull/38267)
- fix(rerank): emit latency and cost headers on /rerank by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;35419](https://github.com/BerriAI/litellm/pull/35419)
- perf(streaming): add shared JSONFragmentAccumulator for Vertex and Anthropic by [@&#8203;deepanshululla](https://github.com/deepanshululla) in [#&#8203;36610](https://github.com/BerriAI/litellm/pull/36610)
- fix(together\_ai): strip internal thinking fields from outbound messages, keep reasoning\_content by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38275](https://github.com/BerriAI/litellm/pull/38275)
- fix(router): persist attempted\_fallbacks and original\_model\_group into spend logs metadata by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38107](https://github.com/BerriAI/litellm/pull/38107)
- fix(logging): redact tool call arguments to valid JSON and preserve null content by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38182](https://github.com/BerriAI/litellm/pull/38182)
- fix(ui): stack policy flow builder below the popup layer so guardrail options render by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38273](https://github.com/BerriAI/litellm/pull/38273)
- test: gate the test tree on B003 so a test cannot swap os.environ for a plain dict by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38274](https://github.com/BerriAI/litellm/pull/38274)
- refactor(repositories): type prisma table access with one generic protocol by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38205](https://github.com/BerriAI/litellm/pull/38205)
- fix(anthropic): buffer streamed responses carrying server-fulfilled tools so retrieval tool calls never reach the client by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;36245](https://github.com/BerriAI/litellm/pull/36245)
- test(together\_ai): regression suite across chat, responses, and messages surfaces by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38283](https://github.com/BerriAI/litellm/pull/38283)
- feat(logging): add async\_post\_call\_failure\_deployment\_hook by [@&#8203;deepanshululla](https://github.com/deepanshululla) in [#&#8203;36657](https://github.com/BerriAI/litellm/pull/36657)
- chore: bump litellm-enterprise 0.1.59 -> 0.1.60, litellm 1.99.0 -> 1.100.0 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38243](https://github.com/BerriAI/litellm/pull/38243)
- test(e2e): cover Together AI reasoning, tool calls, template kwargs, and cost through a live proxy by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38286](https://github.com/BerriAI/litellm/pull/38286)
- fix(logging): keep tracebacks for provider-originated 4xx errors by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38296](https://github.com/BerriAI/litellm/pull/38296)
- chore(ci): promote internal staging to main by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38293](https://github.com/BerriAI/litellm/pull/38293)
- refactor(ui): install the shadcn alert primitive by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38300](https://github.com/BerriAI/litellm/pull/38300)
- refactor(ui): re-pull label, textarea, separator and skeleton from the registry by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38302](https://github.com/BerriAI/litellm/pull/38302)
- feat(prometheus): configure deployment caller identity by [@&#8203;mphilippnv](https://github.com/mphilippnv) in [#&#8203;38221](https://github.com/BerriAI/litellm/pull/38221)
- test(e2e): let the Together replayed-reasoning case survive a single provider miss by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38314](https://github.com/BerriAI/litellm/pull/38314)
- fix(otel): map /v1/messages provider errors before failure logging by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38310](https://github.com/BerriAI/litellm/pull/38310)
- fix(exceptions): map upstream status codes for providers with no exception\_type branch by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38318](https://github.com/BerriAI/litellm/pull/38318)
- fix(passthrough): record ownership of streamed responses under managed ids by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38320](https://github.com/BerriAI/litellm/pull/38320)
- fix(proxy): encrypt streamed responses ids on /openai/v1/responses and /responses aliases by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38325](https://github.com/BerriAI/litellm/pull/38325)
- test(cost-calc): pin the rate fallbacks inside a tiered-pricing tier by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38327](https://github.com/BerriAI/litellm/pull/38327)
- test(e2e): cover the Bedrock provider-feature cells customers run by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38232](https://github.com/BerriAI/litellm/pull/38232)
- fix(together\_ai): fail open on response\_format instead of dropping it for unregistered models by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38269](https://github.com/BerriAI/litellm/pull/38269)
- fix(proxy): honor DATABASE\_DISABLE\_PREPARED\_STATEMENTS in componentized entrypoints by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38363](https://github.com/BerriAI/litellm/pull/38363)
- fix(anthropic-responses): preserve structured output strictness by [@&#8203;eugene-yao-zocdoc](https://github.com/eugene-yao-zocdoc) in [#&#8203;38211](https://github.com/BerriAI/litellm/pull/38211)
- chore(typing): roll up the daily tech debt cleanups from Aug 20 to Aug 26 by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37922](https://github.com/BerriAI/litellm/pull/37922)
- fix(router): resolve hidden aliases for explicit lookup by [@&#8203;daniel-meismer-zocdoc](https://github.com/daniel-meismer-zocdoc) in [#&#8203;38272](https://github.com/BerriAI/litellm/pull/38272)
- fix(ui): keep focus in the add model public name input while typing by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38366](https://github.com/BerriAI/litellm/pull/38366)
- fix(model\_prices): price 1-hour cache writes on claude-3-haiku and claude-3-opus at 2x input by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38371](https://github.com/BerriAI/litellm/pull/38371)
- fix(proxy): keep the caller's Google token on credential-less Vertex passthrough under custom auth by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38299](https://github.com/BerriAI/litellm/pull/38299)
- fix(mcp): preserve provider access token lifetime by [@&#8203;daniel-meismer-zocdoc](https://github.com/daniel-meismer-zocdoc) in [#&#8203;38271](https://github.com/BerriAI/litellm/pull/38271)
- chore(ui): remove stale "New" badges from the dashboard by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38374](https://github.com/BerriAI/litellm/pull/38374)
- test(cost-estimate): pin the prices and period totals /cost/estimate returns by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38315](https://github.com/BerriAI/litellm/pull/38315)
- fix(ci): let the mutation workflow find covered lines so it generates mutants by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38305](https://github.com/BerriAI/litellm/pull/38305)
- fix(model\_prices): raise bedrock\_mantle gpt-5.5 and gpt-5.4 max\_input\_tokens to Mantle's enforced [`1050000`](https://github.com/BerriAI/litellm/commit/1050000) by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38368](https://github.com/BerriAI/litellm/pull/38368)
- fix(azure/realtime): authenticate realtime websocket with Azure AD token when no api-key by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;34658](https://github.com/BerriAI/litellm/pull/34658)
- fix(bedrock): map reasoning\_effort to reasoning.effort for OpenAI GPT-5.x on Converse by [@&#8203;6matt](https://github.com/6matt) in [#&#8203;38279](https://github.com/BerriAI/litellm/pull/38279)
- test(prometheus): cover caller-identity config failure cases by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38380](https://github.com/BerriAI/litellm/pull/38380)
- fix(redis): support credential providers across clients by [@&#8203;eugene-yao-zocdoc](https://github.com/eugene-yao-zocdoc) in [#&#8203;38094](https://github.com/BerriAI/litellm/pull/38094)
- fix(health): support `mode: image_edit` in health checks by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38291](https://github.com/BerriAI/litellm/pull/38291)
- fix(cost-map): add US data residency uplift to claude-sonnet-4-6 and mythos entries by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38369](https://github.com/BerriAI/litellm/pull/38369)
- fix(anthropic): raise missing-credential error on /v1/messages passthrough by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38240](https://github.com/BerriAI/litellm/pull/38240)
- fix(mcp): complete DCR bridge OAuth challenges by [@&#8203;daniel-meismer-zocdoc](https://github.com/daniel-meismer-zocdoc) in [#&#8203;37384](https://github.com/BerriAI/litellm/pull/37384)
- test(proxy): pin the request-validation contracts in proxy/\_types.py by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38307](https://github.com/BerriAI/litellm/pull/38307)
- docs(CLAUDE.md): add pull-before-work rule by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38386](https://github.com/BerriAI/litellm/pull/38386)
- fix(anthropic): scale cache costs by fast mode and trust served speed by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38378](https://github.com/BerriAI/litellm/pull/38378)
- fix(pricing): add azure gpt-5.6 cache write rates and correct data zone priority by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38370](https://github.com/BerriAI/litellm/pull/38370)
- docs: tighten the pull-before-work rule in CLAUDE.md by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38389](https://github.com/BerriAI/litellm/pull/38389)
- fix(health): strip credential fields from GET /health output by [@&#8203;Siraj637909](https://github.com/Siraj637909) in [#&#8203;37090](https://github.com/BerriAI/litellm/pull/37090)
- fix(minimax): attach MINIMAX\_API\_KEY on anthropic messages requests by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38393](https://github.com/BerriAI/litellm/pull/38393)
- refactor(ui): replace hand-picked z-index values with one named scale and lint it by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38282](https://github.com/BerriAI/litellm/pull/38282)
- fix(health): probe Azure GA realtime path for transcription-only models by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38390](https://github.com/BerriAI/litellm/pull/38390)
- fix(bedrock): parse cacheDetails for Converse 1h/5m cache write cost split by [@&#8203;danielva-monday](https://github.com/danielva-monday) in [#&#8203;36762](https://github.com/BerriAI/litellm/pull/36762)
- fix(caching): flush async cache writes cancelled at event loop shutdown by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38385](https://github.com/BerriAI/litellm/pull/38385)
- fix(router): resolve model\_group\_alias before pre-routing strategy dispatch by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38382](https://github.com/BerriAI/litellm/pull/38382)
- feat(proxy): enforce rpm/tpm on model add + fix validation error title in UI by [@&#8203;kunal2002](https://github.com/kunal2002) in [#&#8203;36518](https://github.com/BerriAI/litellm/pull/36518)
- refactor(ui): move every page header onto the shared PageHeader by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38306](https://github.com/BerriAI/litellm/pull/38306)
- fix(proxy): stop cache eviction errors from failing /key/update by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38308](https://github.com/BerriAI/litellm/pull/38308)
- fix(aiohttp): honor global ssl\_verify on the aiohttp\_openai handler path by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38400](https://github.com/BerriAI/litellm/pull/38400)
- fix(logging\_worker): rescue dequeued logging tasks lost at event loop close by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38394](https://github.com/BerriAI/litellm/pull/38394)
- fix(caching): require the namespace delimiter when checking already-namespaced redis keys by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38403](https://github.com/BerriAI/litellm/pull/38403)
- fix(prompts): reject keyed prompt\_data with prompt\_id and populate prompt version by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38404](https://github.com/BerriAI/litellm/pull/38404)
- fix(cost\_calculator): resolve real cost key when model\_name alias contains '/' by [@&#8203;ksk2023](https://github.com/ksk2023) in [#&#8203;38344](https://github.com/BerriAI/litellm/pull/38344)
- fix(cost-map): correct prompt\_cache\_min\_tokens for Claude Fable 5 and backfill Anthropic re-export entries by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38405](https://github.com/BerriAI/litellm/pull/38405)
- test(azure-ai): pin the 422 retry that drops the field the provider rejected by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38309](https://github.com/BerriAI/litellm/pull/38309)
- refactor(ui): read the auto-router tier set through one row list by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38408](https://github.com/BerriAI/litellm/pull/38408)
- fix(proxy): stop empty DB router\_settings lists from clobbering yaml fallbacks by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38406](https://github.com/BerriAI/litellm/pull/38406)
- fix(team): allow no-reset default budgets for team members by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37708](https://github.com/BerriAI/litellm/pull/37708)
- fix: forward image content lists to DeepSeek vision models by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38397](https://github.com/BerriAI/litellm/pull/38397)
- fix(fireworks\_ai): stop using the trace id as the session affinity key by [@&#8203;Hamjaster](https://github.com/Hamjaster) in [#&#8203;35754](https://github.com/BerriAI/litellm/pull/35754)
- fix(gemini-realtime): keep the client's voice on Vertex AI native-audio Live by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38395](https://github.com/BerriAI/litellm/pull/38395)
- fix(vertex\_ai): bill Gemini grounding per unique web search query by [@&#8203;ousamabenyounes](https://github.com/ousamabenyounes) in [#&#8203;36397](https://github.com/BerriAI/litellm/pull/36397)
- fix(health): make the image\_edit health probe moderation-safe by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38417](https://github.com/BerriAI/litellm/pull/38417)
- test: gate the test tree on fifteen assertion and handler rules it already satisfies by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38361](https://github.com/BerriAI/litellm/pull/38361)
- fix(proxy): key lazy openapi stubs off registered features, not sys.modules by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38416](https://github.com/BerriAI/litellm/pull/38416)
- fix(proxy): derive auto-router health from its underlying models by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38174](https://github.com/BerriAI/litellm/pull/38174)
- fix(responses): let cache-control injection reach the system prompt from instructions by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38120](https://github.com/BerriAI/litellm/pull/38120)
- fix(gemini): bill Google Maps grounding as its own SKU by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38418](https://github.com/BerriAI/litellm/pull/38418)
- fix(speech): keep proxy metadata and completion cost through the TTS completion bridge by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38414](https://github.com/BerriAI/litellm/pull/38414)
- fix: map Gemini ON\_DEMAND\_FLEX traffic type to flex service tier by [@&#8203;bisma-nawaz](https://github.com/bisma-nawaz) in [#&#8203;37724](https://github.com/BerriAI/litellm/pull/37724)
- feat(langfuse): support langfuse\_environment as a per-key dynamic callback param by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38264](https://github.com/BerriAI/litellm/pull/38264)
- feat(proxy): hide unhealthy models from model listings, opt-in by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38313](https://github.com/BerriAI/litellm/pull/38313)
- fix(mcp): honor admin-entered OAuth URLs on authorize after issuer yield by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38379](https://github.com/BerriAI/litellm/pull/38379)
- fix(model\_prices): correct gemini-3.5-flash-lite flex cache-read pricing by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38422](https://github.com/BerriAI/litellm/pull/38422)
- fix(cost): price gemini-live-2.5-flash-native-audio realtime sessions by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38419](https://github.com/BerriAI/litellm/pull/38419)
- fix(cost-map): correct Gemini TTS and native-audio rates by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38412](https://github.com/BerriAI/litellm/pull/38412)
- fix(prompts): propagate PATCHed prompt templates to every worker and pod by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38411](https://github.com/BerriAI/litellm/pull/38411)
- fix(model\_prices): bill gemini -latest/preview alias cache reads at 10% of input by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38423](https://github.com/BerriAI/litellm/pull/38423)
- fix(proxy): sync search tools into the router on management writes by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38392](https://github.com/BerriAI/litellm/pull/38392)
- feat(guardrails): track Azure Prompt Shield usage and cost with spend isolation by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38387](https://github.com/BerriAI/litellm/pull/38387)
- fix(prompts): apply prompt templates before routing on /v1/responses and honor ignore\_prompt\_manager\_model by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38407](https://github.com/BerriAI/litellm/pull/38407)
- fix(cost): make cost-breakdown headers respect service tier by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38424](https://github.com/BerriAI/litellm/pull/38424)
- fix(mcp): add litellm\[mcp] extra and actionable error when streamable\_http\_client is missing by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38399](https://github.com/BerriAI/litellm/pull/38399)
- revert(proxy): remove router\_model\_name from auto-routed response bodies by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38429](https://github.com/BerriAI/litellm/pull/38429)
- fix(google\_genai): price streamed generateContent with the provider that served it by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36055](https://github.com/BerriAI/litellm/pull/36055)
- fix(logging): stop billing and logging response reads as LLM calls by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;36890](https://github.com/BerriAI/litellm/pull/36890)
- fix(budget): serialize model\_max\_budget before the /budget/update write by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38430](https://github.com/BerriAI/litellm/pull/38430)
- fix(ui): block the auto-router submit on a missing classifier model and an orphaned keyword rule by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38427](https://github.com/BerriAI/litellm/pull/38427)
- feat(complexity\_router): heuristic-first classifier chaining by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38428](https://github.com/BerriAI/litellm/pull/38428)
- test(e2e): un-skip the per-model budget update case by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38437](https://github.com/BerriAI/litellm/pull/38437)
- fix(cost): stop double-billing cached tokens that overlap a modality by [@&#8203;Srivatsa03](https://github.com/Srivatsa03) in [#&#8203;37407](https://github.com/BerriAI/litellm/pull/37407)
- feat(ui): add Teams list CSV export with budgets, model grants, and rate limits by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38436](https://github.com/BerriAI/litellm/pull/38436)
- fix(mcp): accept raw x-litellm-api-key on streamable HTTP admission by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38364](https://github.com/BerriAI/litellm/pull/38364)
- fix: bound row count on GET /spend/logs to stop unbounded LiteLLM\_SpendLogs scans by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38420](https://github.com/BerriAI/litellm/pull/38420)
- fix(scim): apply default\_team\_params (incl. models) to SCIM-created teams by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38433](https://github.com/BerriAI/litellm/pull/38433)
- fix(prompts): propagate prompt deletes to every worker and pod by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38434](https://github.com/BerriAI/litellm/pull/38434)
- fix(anthropic\_adapter): carry web search cost into /v1/messages breakdown headers by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38439](https://github.com/BerriAI/litellm/pull/38439)
- fix(ui): show custom technical keywords on every router whose scorer runs by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38451](https://github.com/BerriAI/litellm/pull/38451)
- fix(e2e): move the vertex realtime suite off the retired Live preview model by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38454](https://github.com/BerriAI/litellm/pull/38454)
- feat(newrelic): per-team cost and usage metrics via team callbacks by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;37610](https://github.com/BerriAI/litellm/pull/37610)
- fix(ui): carry a preset's per-tier litellm\_params through the prefill by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38453](https://github.com/BerriAI/litellm/pull/38453)
- fix(e2e): size the mid-conversation-system cache prefix above the minimum deterministically by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38468](https://github.com/BerriAI/litellm/pull/38468)
- fix(e2e): disable thinking on the gemini chat cost test instead of racing its budget by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38469](https://github.com/BerriAI/litellm/pull/38469)
- feat(ui): put the auto-router savings hero on a spend rail and a four-tile row by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38470](https://github.com/BerriAI/litellm/pull/38470)
- feat(ui): toggle internal health check visibility in request logs by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38391](https://github.com/BerriAI/litellm/pull/38391)
- fix(mcp): canonicalize bearer scheme on bridge egress by [@&#8203;daniel-meismer-zocdoc](https://github.com/daniel-meismer-zocdoc) in [#&#8203;38398](https://github.com/BerriAI/litellm/pull/38398)
- refactor: clean up fresh tech debt from 2026-08-27 window by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38484](https://github.com/BerriAI/litellm/pull/38484)
- fix(exception\_mapping\_utils): map unmapped exceptions when model and provider are unset by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38496](https://github.com/BerriAI/litellm/pull/38496)
- fix(ui\_sso): resolve highest privilege Entra app role, not first in claim by [@&#8203;imranismail](https://github.com/imranismail) in [#&#8203;36728](https://github.com/BerriAI/litellm/pull/36728)
- fix(proxy): regenerate lazy OpenAPI snapshot and guard it in CI by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38410](https://github.com/BerriAI/litellm/pull/38410)
- feat(ui): add cache hit/miss filter to Request Logs by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38432](https://github.com/BerriAI/litellm/pull/38432)
- fix(bedrock): sign rerank requests with the shared header-filtered SigV4 helper (internal copy of [#&#8203;36462](https://github.com/BerriAI/litellm/issues/36462)) by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38093](https://github.com/BerriAI/litellm/pull/38093)
- fix(bedrock): sign rerank requests with the shared, header-filtered SigV4 helper by [@&#8203;noahnistler](https://github.com/noahnistler) in [#&#8203;36462](https://github.com/BerriAI/litellm/pull/36462)
- fix(ui): order the auto-routers table newest first so a new router lands on page one by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38545](https://github.com/BerriAI/litellm/pull/38545)
- feat(ui): run the Anthropic Family preset's reasoning tier on Opus 5 at high thinking by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38490](https://github.com/BerriAI/litellm/pull/38490)
- build(ui): bump nginx to 1.31-alpine by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38541](https://github.com/BerriAI/litellm/pull/38541)
- feat(otel): support per-team/per-key service.name for OTel v2 destinations by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38532](https://github.com/BerriAI/litellm/pull/38532)
- test(e2e): de-flake the cost-header cache read and the router fallback control by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38435](https://github.com/BerriAI/litellm/pull/38435)
- feat(gemini): day-0 support for gemini-3.5-transcribe and transcribe-live by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38540](https://github.com/BerriAI/litellm/pull/38540)
- feat(health): opt-in model-group allowlist for background health checks and health-check routing by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38539](https://github.com/BerriAI/litellm/pull/38539)
- fix(mcp): keep upstream OAuth Authorization when jwt signer hook injects one on tools/call by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38555](https://github.com/BerriAI/litellm/pull/38555)
- fix: suppress misleading register\_model unresolved-cost warnings for entries without custom pricing by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38542](https://github.com/BerriAI/litellm/pull/38542)
- feat(proxy): opt-in budget rollover carrying overage into the next window by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38514](https://github.com/BerriAI/litellm/pull/38514)
- fix(auth): skip guaranteed-miss team lookup for the litellm-dashboard sentinel by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38471](https://github.com/BerriAI/litellm/pull/38471)
- fix(key\_management): allow /key/update to keep or shrink MCP server grants the key already holds by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38463](https://github.com/BerriAI/litellm/pull/38463)
- fix: keep schema reconciliation from fighting a partitioned LiteLLM\_SpendLogs by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38452](https://github.com/BerriAI/litellm/pull/38452)
- fix(ui): open select popups below the trigger instead of over it by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38554](https://github.com/BerriAI/litellm/pull/38554)
- fix(realtime): bill trailing audio when a Gemini transcribe Live session closes by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38563](https://github.com/BerriAI/litellm/pull/38563)
- test(e2e): cover key generate and update on the Admin UI path by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38448](https://github.com/BerriAI/litellm/pull/38448)
- chore: bump litellm-enterprise 0.1.60 -> 0.1.61, litellm-proxy-extras 0.4.89 -> 0.4.90 by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38566](https://github.com/BerriAI/litellm/pull/38566)
- fix(ui): let the paginated search select keep what the user types by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38475](https://github.com/BerriAI/litellm/pull/38475)
- fix(otel): anchor MCP tool-call spans to the gateway's own trace, link the client's context by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38317](https://github.com/BerriAI/litellm/pull/38317)
- fix: roll up the open deflake fixes for the MCP logging queue, PTU rollup, license gate, and pricing test isolation by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;37833](https://github.com/BerriAI/litellm/pull/37833)
- fix(model\_prices): rolling registry audit - verified models and rates for Novita, DeepInfra, W\&B, Bedrock Sol, Gemini, Fireworks, Azure gpt-5.6, Mistral, Together by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38207](https://github.com/BerriAI/litellm/pull/38207)
- test(together\_ai): assert fail-open supported params for models missing from the registry by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38487](https://github.com/BerriAI/litellm/pull/38487)
- test(e2e): let the together tool tests accept parallel calls by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38567](https://github.com/BerriAI/litellm/pull/38567)
- feat(mcp): let a resolved OAuth token target a custom upstream header by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38456](https://github.com/BerriAI/litellm/pull/38456)
- feat(together\_ai): map reasoning\_effort per model class by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38263](https://github.com/BerriAI/litellm/pull/38263)
- feat(dashscope): support qwen-image-3.0 and qwen-image-3.0-pro image generation by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38449](https://github.com/BerriAI/litellm/pull/38449)
- fix(cost): apply Together AI cache read pricing and per-model registry rates by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38280](https://github.com/BerriAI/litellm/pull/38280)
- fix(guardrails): forward aws\_external\_id when the bedrock guardrail assumes a role by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38376](https://github.com/BerriAI/litellm/pull/38376)
- fix(transcription): synthesize srt/vtt output for adapters without native subtitle formats by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38561](https://github.com/BerriAI/litellm/pull/38561)
- fix(streaming): preserve provider service-tier metadata so Vertex flex streams bill at flex rates by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38458](https://github.com/BerriAI/litellm/pull/38458)
- fix(realtime): bill Gemini Live native-audio output tokens at the audio rate by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38457](https://github.com/BerriAI/litellm/pull/38457)
- fix(anthropic): carry tool\_reference tool results through the guardrail translation round trip by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38465](https://github.com/BerriAI/litellm/pull/38465)
- fix(anthropic-adapter): pass provider-native and OpenAI-format tools through on /v1/messages by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38431](https://github.com/BerriAI/litellm/pull/38431)
- test(e2e): serve the vision image from our own fixture by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38575](https://github.com/BerriAI/litellm/pull/38575)
- feat(together\_ai): add zai-org/GLM-5.3-Flash to the model registry by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38486](https://github.com/BerriAI/litellm/pull/38486)
- fix(ui): stop server-searched comboboxes from clobbering picks and queries by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38574](https://github.com/BerriAI/litellm/pull/38574)
- feat(model\_prices): let a map entry declare its exact reasoning\_effort levels by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38481](https://github.com/BerriAI/litellm/pull/38481)
- fix(anthropic): carry the adaptive effort tier to every bridged Claude target by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38533](https://github.com/BerriAI/litellm/pull/38533)
- feat(alerting): add native Microsoft Teams alerting destination by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38367](https://github.com/BerriAI/litellm/pull/38367)
- chore(proxy): resync the generated API artifacts with the current models by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38587](https://github.com/BerriAI/litellm/pull/38587)
- fix(router): reject complexity-router settings written outside complexity\_router\_config by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38570](https://github.com/BerriAI/litellm/pull/38570)
- feat(ui): session-level cache observability in request logs by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38442](https://github.com/BerriAI/litellm/pull/38442)
- fix(ui): link Virtual Keys hint through the migrated /ui route by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38596](https://github.com/BerriAI/litellm/pull/38596)
- fix(anthropic): carry the effort tier only where the target declares reasoning\_effort by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38592](https://github.com/BerriAI/litellm/pull/38592)
- fix(presidio): chunk oversized text before /analyze so large content blocks do not fail by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38483](https://github.com/BerriAI/litellm/pull/38483)
- fix(logging): stop stream-based log collectors classifying INFO logs as errors by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38476](https://github.com/BerriAI/litellm/pull/38476)
- feat(ui): dry-run an auto-router config against the backend before saving it by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38595](https://github.com/BerriAI/litellm/pull/38595)
- fix(guardrails): add fail-open mode to CrowdStrike AIDR guardrail by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38568](https://github.com/BerriAI/litellm/pull/38568)
- fix(router): copy instead of mutating caller metadata when scrubbing fallback stamp keys by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38586](https://github.com/BerriAI/litellm/pull/38586)
- feat(proxy): opt-in enforce\_fallback\_model\_access authorizes router fallbacks against the calling key by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38572](https://github.com/BerriAI/litellm/pull/38572)
- fix(langfuse): warn and drop invalid LANGFUSE\_TRACING\_ENVIRONMENT instead of failing requests by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38582](https://github.com/BerriAI/litellm/pull/38582)
- fix(tencent): route thinking through extra\_body in chat completions by [@&#8203;FelipeRodriguesGare](https://github.com/FelipeRodriguesGare) in [#&#8203;38100](https://github.com/BerriAI/litellm/pull/38100)
- test-check-commits by [@&#8203;nickhac](https://github.com/nickhac) in [#&#8203;36344](https://github.com/BerriAI/litellm/pull/36344)
- feat(proxy): dry-run a real request body on /auto\_router/test\_routing by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38590](https://github.com/BerriAI/litellm/pull/38590)
- fix(shadow\_eval): refuse a judge model that also serves one of the arms it grades by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38589](https://github.com/BerriAI/litellm/pull/38589)
- fix(anthropic): resolve /v1/messages effort tiers through the capability owner by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38492](https://github.com/BerriAI/litellm/pull/38492)
- fix(router): fall over on raised mid-stream errors in /v1/messages streams by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38606](https://github.com/BerriAI/litellm/pull/38606)
- feat(models): add daily Together AI model registry sync script and workflow by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38257](https://github.com/BerriAI/litellm/pull/38257)
- feat(ui): the model and wire layer for operator-defined auto-router tier sets by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38602](https://github.com/BerriAI/litellm/pull/38602)
- fix(moonshot, together\_ai): send the reasoning effort Kimi K3 accepts by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38611](https://github.com/BerriAI/litellm/pull/38611)
- fix(ui): one-click theme toggle and matching Docs/Blog styling in the top bar by [@&#8203;ryan-crabbe-berri](https://github.com/ryan-crabbe-berri) in [#&#8203;38601](https://github.com/BerriAI/litellm/pull/38601)
- feat(proxy): opt-in flags to require rpm/tpm on model and project create by [@&#8203;ansh-agrawal](https://github.com/ansh-agrawal) in [#&#8203;36514](https://github.com/BerriAI/litellm/pull/36514)
- fix(exceptions): keep a refused connection an APIConnectionError by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38624](https://github.com/BerriAI/litellm/pull/38624)
- chore(ci): promote internal staging to main by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38616](https://github.com/BerriAI/litellm/pull/38616)
- fix(anthropic): drop and self-heal empty thinking blocks on /v1/messages by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38625](https://github.com/BerriAI/litellm/pull/38625)
- fix(anthropic): handle per-level reasoning\_effort flags without supports\_reasoning by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38618](https://github.com/BerriAI/litellm/pull/38618)
- fix(complexity\_router): route client housekeeping calls to the cheapest tier by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38598](https://github.com/BerriAI/litellm/pull/38598)
- test: fix staging CI regressions from [#&#8203;38182](https://github.com/BerriAI/litellm/issues/38182), [#&#8203;38144](https://github.com/BerriAI/litellm/issues/38144), [#&#8203;38265](https://github.com/BerriAI/litellm/issues/38265), [#&#8203;37962](https://github.com/BerriAI/litellm/issues/37962), and [#&#8203;37969](https://github.com/BerriAI/litellm/issues/37969) by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38304](https://github.com/BerriAI/litellm/pull/38304)
- feat(spend): report prompt caching savings as total and gateway-attributed by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38134](https://github.com/BerriAI/litellm/pull/38134)
- fix(proxy): let llm\_api virtual keys read /model\_group/info by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38662](https://github.com/BerriAI/litellm/pull/38662)
- feat(ui): edit the auto-router tier set with custom classifier-defined tiers by [@&#8203;tin-berri](https://github.com/tin-berri) in [#&#8203;38603](https://github.com/BerriAI/litellm/pull/38603)
- fix(proxy): count tools, system, and Anthropic image and document blocks in the count\_tokens fallback (internal copy of [#&#8203;36671](https://github.com/BerriAI/litellm/issues/36671)) by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38657](https://github.com/BerriAI/litellm/pull/38657)
- test: refresh the suites that drifted from langfuse and OpenAI's retired Assistants API by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38637](https://github.com/BerriAI/litellm/pull/38637)
- test(e2e): unskip four tests whose blockers no longer hold by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38640](https://github.com/BerriAI/litellm/pull/38640)
- feat(proxy): add paginated GET /public/v1/model\_hub by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38636](https://github.com/BerriAI/litellm/pull/38636)
- refactor(ui): type search tool params from the generated schema by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38633](https://github.com/BerriAI/litellm/pull/38633)
- feat(a2a): semantic search over the agent registry via GET /v1/agents?query and an agent\_search MCP tool by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38609](https://github.com/BerriAI/litellm/pull/38609)
- fix(model\_prices): add bedrock\_mantle gpt-5.5/5.4 272K tiers, align sol with AWS invoice by [@&#8203;mateo-berri](https://github.com/mateo-berri) in [#&#8203;38615](https://github.com/BerriAI/litellm/pull/38615)
- fix(ui): keep the usage filter visible when the caller's scope is empty by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38581](https://github.com/BerriAI/litellm/pull/38581)
- fix(registry): add Gemini Omni 1.1 Flash, xAI grok-imagine image models, Mistral cache-read pricing, GLM 5.3 Flash + Kimi K2.7 Code entries by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38560](https://github.com/BerriAI/litellm/pull/38560)
- fix(logging): preserve null end user in callbacks by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38642](https://github.com/BerriAI/litellm/pull/38642)
- test: close mutation-testing gaps in container, skills and openai-like config factories by [@&#8203;yuneng-berri](https://github.com/yuneng-berri) in [#&#8203;38677](https://github.com/BerriAI/litellm/pull/38677)
- fix: enforce MCP toolsets attached to a team, org, or internal user by [@&#8203;yucheng-berri](https://github.com/yucheng-berri) in [#&#8203;38488](https://github.com/BerriAI/litellm/pull/38488)
- fix(tests): drain the global logging worker in RAG aquery billing tests by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38653](https://github.com/BerriAI/litellm/pull/38653)
- chore(techdebt): type new signatures and drop slop comments from the last 24h by [@&#8203;devin-ai-integration](https://github.com/devin-ai-integration)\[bot] in [#&#8203;38644](https://github.com/Be…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants